/*

	Thanks to Ross N. Williams for his tutorial on CRC32
	Original CRC32 code by Ross N. Williams
	CRC32 Class by Davide Mauri

*/
#include "crc32.h"

#include <stdio.h>
#include <string.h>
#include <memory>
#include <cassert>

#include "util/mmgr.h"		    // Needs to come in early

using namespace num;

#if defined(__APPPLE__) || defined(_PLATFORM_ANDROID) || defined(_PLATFORM_IOS)
#include <ctype.h>
char *strupr(char *string)
{
	char *s;
	
	if (string)
	{
		for (s = string; *s; ++s)
			*s = toupper(*s);
	}
	return string;
} 
#endif


CRC32&   CRC32::Instance()
{
    static CRC32  Instance;
    return Instance;
}


/*
	Allocate memory and initalize look up table
*/
CRC32::CRC32(void)
{
	crc32_table			= new unsigned long[256];
	crc32_filebuffer	= new char[1024];
	crc32_init();
	crc = 0xffffffff;
}

/*
	Free memory
*/
CRC32::~CRC32(void)
{		
	delete[] crc32_table;
	delete[] crc32_filebuffer;
}

/*
	Swap bit 0 for bit 7, bit 1 for bit 6, etc.
*/
unsigned long CRC32::reflect(unsigned long ref, char ch)
{
	unsigned long value(0);

	for(int i = 1; i < (ch + 1); i++)
	{
		if(ref & 1)
			value |= 1 << (ch - i);
		ref >>= 1;
	}
	return value;
}

/*
	Fill the look up table
*/
void CRC32::crc32_init(void)
{
	int i, j;
	
	for(i = 0; i <= 0xFF; i++)
	{
		crc32_table[i] = (reflect((unsigned long)i, 8) << 24);
		
		for (j = 0; j < 8; j++)
			crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (1 << 31) ? CRC32_POLY : 0);

		crc32_table[i] = reflect(crc32_table[i], 32);
	}
}

/*
	Calculate the crc32 of a memory block
*/
unsigned long CRC32::crc32_calc(const char* buf, unsigned int len)
{
	unsigned long tcrc;

	//tcrc = crc;
    tcrc = 0xffffffff;

	while (len--)
		tcrc = (tcrc >> 8) ^ crc32_table[(tcrc & 0xFF) ^ *buf++];

	return tcrc;            
}

/*
	Calculate the crc32 for a file
*/
/*unsigned long CRC32::calculate(FILE *src)
{
#ifdef _PLATFORM_WIN32
    assert(src);
	unsigned int rcount;

	//Initialize CRC32 value
	crc = 0xffffffff;

	//Read data from source and calculate
	//	CRC32 incrementally
	rewind(src);
	while (!feof(src)) 
	{
		memset(crc32_filebuffer, 0, 1024);
		rcount = (unsigned int) fread(crc32_filebuffer, (int)sizeof(char), 1024, src);			
		crc = crc32_calc(crc32_filebuffer, rcount); 
	}
	rewind(src);
	
	// Finalize CRC32 calculation 
	
	crc = crc^0xffffffff;

	return crc;
}*/

/*
	Get the crc32 value
*/
unsigned long CRC32::get(void)
{
	return crc;
}


/*static unsigned long FastHashGen( const char* pToken, unsigned long keyValue = 17 )
{
    if ( NULL != pToken )
    {
        const char* name = pToken;
        while( *pToken )
        {
            keyValue *= 65599;
            keyValue = (keyValue ^ *pToken);
            pToken++;
        }
        return( keyValue );
    }
    return keyValue;
}*/

unsigned long	CRC32::FastHash(unsigned char*	DataA, unsigned int Size)
{
	unsigned int	keyValue = 17;
	for (unsigned int i=0; i<Size; i++)
	{
	    unsigned char* pToken = &DataA[i];
        keyValue *= 65599;
        keyValue = (keyValue ^ *pToken);
	}
    return( keyValue );
}

unsigned long	CRC32::FastHash(const std::string& Str, bool	ToUppr )
{
	unsigned int	keyValue = 17;
	if (Str.empty()) return keyValue;
	if (ToUppr)
	{
        const char* pToken = &Str[0];
        while( *pToken )
        {
            keyValue *= 65599;
            keyValue = (keyValue ^ toupper(*pToken));
            pToken++;
        }
        return( keyValue );
	} else
	{
        const char* pToken = &Str[0];
        while( *pToken )
        {
            keyValue *= 65599;
            keyValue = (keyValue ^ *pToken);
            pToken++;
        }
        return( keyValue );
	}
}

unsigned long	CRC32::StrCRC(const std::string& _Str, bool _UpperCase )
{
	return StrCRC(_Str.c_str(), _UpperCase);
}


unsigned long	CRC32::StrCRC(const char* _szStr, bool _bUpperCase)
{
	assert(_szStr);
	char	szString[1024];
	strcpy(szString, _szStr);
	if (_bUpperCase)
	{
		strupr(szString);
		return crc32_calc(szString, (unsigned int)(strlen(szString)*(int)sizeof(char)));
	}
	return crc32_calc(szString, (unsigned int)(strlen(szString)*(int)sizeof(char)));

/*	assert(_szStr);
	if (_bUpperCase)
	{
		char	szString[1024];
		strcpy(szString, _szStr);
		strupr(szString);
		return FastHashGen(szString);
	}
	return FastHashGen(_szStr);*/
}
